package com.plum.admin.common.datascope;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.plum.admin.common.enums.DataScopeTypeEnum;
import com.plum.admin.common.exception.BaseException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;

/**
 * TODO
 *
 * @author 子丶沫
 * @version 1.0
 * @date 2021/8/17 10:37
 */
@Slf4j
@AllArgsConstructor
@Component
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class,Integer.class})})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {
    private final DataSource dataSource;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(log.isDebugEnabled()){
            log.info("进入拦截器");
        }
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        // SQL 解析
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }
        // 针对定义了rowBounds，做为mapper接口方法的参数
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        // 执行的SQL语句
        String originalSql = boundSql.getSql();
        if(originalSql.contains("COUNT(")){
            return invocation.proceed();
        }
        // SQL语句的参数
        Object parameterObject = boundSql.getParameterObject();
        DataScope dataScope = findDataScopeObject(parameterObject);
        if(ObjectUtil.isNull(dataScope)){
            return invocation.proceed();
        }
        String scopeName = dataScope.getScopeName();
        List<String> deptIds = dataScope.getDeptIds();
        if(CollectionUtil.isEmpty(deptIds)){
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if(ObjectUtil.isNull(authentication)){
                throw new BaseException("您还未登录");
            }
            User user = (User) authentication.getPrincipal();
            if(ObjectUtil.isNull(user)){
                throw new BaseException("user is not login");
            }
            List<String> roleIds = user
                    .getAuthorities()
                    .stream()
                    .map(GrantedAuthority::getAuthority)
                    .filter(authority -> authority.startsWith("ROLE_"))
                    .map(authority -> authority.split("_")[1])
                    .collect(Collectors.toList());
            Entity entity = Db.use(dataSource).query("SELECT * from sys_role where id IN (" + CollectionUtil.join(roleIds, ",") + ")")
                    .stream().min(Comparator.comparingInt(o -> o.getInt("ds_type"))).get();
            Integer dsType = entity.getInt("ds_type");
            if(dsType== DataScopeTypeEnum.ALL.getType()){
                return invocation.proceed();
            }
            String dsScope = entity.getStr("ds_scope");
            deptIds.addAll(Arrays.stream(dsScope.split(","))
                    .collect(Collectors.toList()));
            String join = CollectionUtil.join(deptIds, ",");
            originalSql="SELECT * from ("+originalSql+") temp_data_scope where temp_data_scope."+scopeName+" IN ("+join+")";
            metaObject.setValue("delegate.boundSql.sql",originalSql);
            return  invocation.proceed();
        }
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
    /**
     * 查找参数是否包括DataScope对象
     *
     * @param parameterObj 参数列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }
}
